﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SudokuGame.Sudoku
{
    public class SudokuResolver
    {
        /// <summary>
        /// Vyřeší zadanou hru Sudoku.
        /// </summary>
        /// <param name="game">Zadaná hra</param>
        /// <param name="showProgressAction">Metoda, do níž se pošle vždy jeden krok řešení, aby uživatel měl na co koukat. Pozor, může být null.</param>
        /// <returns>Seznam všech správných řešení</returns>
        public static List<SudokuSheet> Resolve(SudokuSheet game, SudokuProgressHandler showProgressAction)
        {
            List<SudokuSheet> result = new List<SudokuSheet>();
            SudokuSheet test = game.Clone;

            string[,] pole = new string[9, 9];

            for (int i = 0; i < 9; i++)
            {
                for (int j = 0; j < 9; j++)
                {
                    if (test[i, j] == null)
                        pole[i, j] = "123456789";
                    else
                        pole[i, j] = test[i, j].ToString();
                }
            }


            bool end = false;
            int e = 0;
            while (e != 10000 && !end)
            {
                end = true;
                e++;
                for (int i = 0; i < 9; i++)
                {
                    for (int j = 0; j < 9; j++)
                    {
                        if (pole[i, j].Length != 1)
                        {
                            end = false;
                            pole[i, j] = VratVarianty(i, j, pole);
                        }

                    }
                }
                //Dopocet
                pole = Krajeni(pole);


                //duplicity řádků 
                pole = DuplicityRadkuASloupcu(pole);

            }


            pole = DuplicityRadkuASloupcu(pole);

            for (int i = 0; i < 9; i++)
            {
                for (int j = 0; j < 9; j++)
                {
                    if (!test.Cells[i, j].IsFixed)
                        test[i, j] = Convert.ToInt32(pole[i, j]);
                }
            }

                
            result.Add(test);
            return result;
        }

        /// <summary>
        /// Metoda doplňování čísel 
        /// </summary>
        /// <param name="pole"></param>
        /// <returns></returns>
         private static string[,] Krajeni(string[,] pole)
         {
             //aby proběhlo pro všechny čísla cislo = aktualní číslo
             for (int cislo = 1; cislo < 9; cislo++)
             {
                 for (int e = 0; e < 3; e++)
                 {
                    

                     //první sektor
                     int[] x = {-1, -1, -1};
                     int[] y = {-1, -1, -1};

                     for (int i = e * 3; i < e * 3 + 3; i++)
                     {
                         for (int j = 0; j < 3; j++)
                         {
                             if (pole[i, j] == cislo.ToString())
                             {
                                 x[0] = i;
                                 y[0] = j;
                             }
                         }
                     }

                     //druhy sektor
                     for (int i = e * 3; i < e * 3 + 3; i++)
                     {
                         for (int j = 3; j < 6; j++)
                         {
                             if (pole[i, j] == cislo.ToString())
                             {
                                 x[1] = i;
                                 y[1] = j;
                             }
                         }
                     }
                     //treti sektor
                     for (int i = e * 3; i < e * 3 + 3; i++)
                     {
                         for (int j = 6; j < 9; j++)
                         {
                             if (pole[i, j] == cislo.ToString())
                             {
                                 x[2] = i;
                                 y[2] = j;
                             }
                         }
                     }

                     int nenajitaCiala = 0;
                     int nenajityIndex = 0;
                     for (int i = 0; i < 3; i++)
                     {
                         if (x[i] == -1)
                         {
                             nenajityIndex = i;
                             nenajitaCiala++;
                         }
                     }


                     if (nenajitaCiala == 1)
                     {
                         int pocetNalezenychVeSloupci = 0;
                         int index = 0;
                         for (int i = nenajityIndex*3; i < nenajityIndex*3 + 3; i++)
                         {

                             for (int j = 0; j < 9; j++)
                             {
                                 if (pole[j, i] == cislo.ToString())
                                 {
                                     pocetNalezenychVeSloupci++;
                                     index = i;
                                 }
                             }

                         }


                         if (pocetNalezenychVeSloupci == 1)
                             pole[index, nenajityIndex] = cislo.ToString();

                     }
                 }

             }






            return pole;
         }

        /// <summary>
        /// Vrátí možné varinaty pro žádek a sloupec
        /// </summary>
        /// <param name="rad">řádek</param>
        /// <param name="slou">sloupec</param>
        /// <returns></returns>
        private static string VratVarianty(int rad,int slou,string[,] pole)
        {
            string moznosti = pole[rad, slou];
            //radek
            for (int j = 0; j < 9;j++ )
            {
                if (pole[rad, j].Length == 1)
                {
                    moznosti=OdstranCislo(moznosti, pole[rad, j]);
                }
            }
            


            //sloupec
            for (int j = 0; j < 9; j++)
            {
                if (pole[j,slou].Length == 1)
                {
                    moznosti = OdstranCislo(moznosti, pole[j, slou]);
                }

            }

            //řešení sektorů
            moznosti = ZeSektoru(rad,slou, pole,moznosti);




            return moznosti;
        }
        
        private static string[,] DuplicityRadkuASloupcu(string[,] pole)
        {
            
            for (int i = 0; i < 9; i++)
            {
                //celí radek/sloupec
                string celek = string.Empty;
                int index;

                for (int j = 0; j < 9; j++)
                {
                    if (pole[i, j].Length != 1)
                    {
                        celek += pole[i, j];
                    }

                }

                for (int j = 1; j < 10; j++)
                {
                    int pocetDuplicit = 0;

                    for (int k = 0; k < celek.Length; k++)
                    {
                        if (celek[k] == j)
                        {
                            pocetDuplicit++;
                        }
                        
                    }

                    if (pocetDuplicit == 1)
                    {
                        for (int e = 0; e < 9; e++)
                        {
                            for (int f = 0; f < pole[i,e].Length; f++)
                            {
                                if(pole[i,e][f].ToString()==j.ToString())
                                {
                                    pole[i, e] = j.ToString();

                                }

                            }
                        }
                    }
                    
                }


            }


            return pole;
        }


        /// <summary>
        /// Vrátí možné čísla ze sektorů podle řádu a sloupce
        /// </summary>
        /// <param name="rad">Řádek</param>
        /// <param name="slou">Sloupec</param>
        /// <param name="pole"></param>
        /// <param name="moznosti"></param>
        /// <returns></returns>
        private  static string ZeSektoru(int rad,int slou,string[,] pole,string moznosti)
        {
            /*
             * |1|2|3|
             * |4|5|6|
             * |7|8|9|
             */
            //zjisteni sektoru
            if (rad<3&&slou<3)
            {
                //sektor 1
                for (int i = 0; i < 3; i++)
                {
                    for (int j = 0; j < 3; j++)
                    {
                        if (pole[i, j].Length == 1)
                            moznosti = OdstranCislo(moznosti, pole[i, j]);

                    }
                }
            }
            else if (rad < 6 && slou < 3)
            {
                //sektor 2
                for (int i = 3; i < 6; i++)
                {
                    for (int j = 0; j < 3; j++)
                    {
                        if (pole[i, j].Length == 1)
                            moznosti = OdstranCislo(moznosti, pole[i, j]);

                    }
                }
            }
            else if (rad < 9 && slou < 3)
            {
                //sektor 3
                for (int i = 6; i < 9; i++)
                {
                    for (int j = 0; j < 3; j++)
                    {
                        if (pole[i, j].Length == 1)
                            moznosti = OdstranCislo(moznosti, pole[i, j]);

                    }
                }
            }
            else if (rad < 3 && slou < 6)
            {
                //sektor 4
                for (int i = 0; i < 3; i++)
                {
                    for (int j = 3; j < 6; j++)
                    {
                        if (pole[i, j].Length == 1)
                            moznosti = OdstranCislo(moznosti, pole[i, j]);

                    }
                }
            }
            else if (rad < 6 && slou < 6)
            {
                //sektor 5
                for (int i = 3; i < 6; i++)
                {
                    for (int j = 3; j < 6; j++)
                    {
                        if (pole[i, j].Length == 1)
                            moznosti = OdstranCislo(moznosti, pole[i, j]);

                    }
                }
            }
            else if (rad < 9 && slou < 6)
            {
                //sektor 6
                for (int i = 6; i < 9; i++)
                {
                    for (int j = 3; j < 6; j++)
                    {
                        if (pole[i, j].Length == 1)
                            moznosti = OdstranCislo(moznosti, pole[i, j]);

                    }
                }
            }
            else if (rad < 3 && slou < 6)
            {
                //sektor 7
                for (int i = 0; i < 3; i++)
                {
                    for (int j = 6; j < 9; j++)
                    {
                        if (pole[i, j].Length == 1)
                            moznosti = OdstranCislo(moznosti, pole[i, j]);

                    }
                }
            }
            else if (rad < 6 && slou < 6)
            {
                //sektor 8
                for (int i = 3; i < 6; i++)
                {
                    for (int j = 6; j < 9; j++)
                    {
                        if (pole[i, j].Length == 1)
                            moznosti = OdstranCislo(moznosti, pole[i, j]);

                    }
                }
            }
            else if (rad < 9 && slou < 6)
            {
                //sektor 9
                for (int i = 6; i < 9; i++)
                {
                    for (int j = 6; j < 9; j++)
                    {
                        if (pole[i, j].Length == 1)
                            moznosti = OdstranCislo(moznosti, pole[i, j]);

                    }
                }
            }


            return moznosti;
        }
        
        

        private static string OdstranCislo(string zdroj, string odstran)
        {
           
            for (int j = 0; j < odstran.Length; j++)
            {
                zdroj=_Odstranovac(zdroj, odstran[j].ToString());
            }

            return zdroj;
        }

        private  static string _Odstranovac(string zdroj,string odstran)
        {
            string vysledek = string.Empty;

           
                for (int j = 0; j < zdroj.Length;j++ )
                {
                    if(zdroj[j].ToString()!=odstran)
                    {
                        vysledek += zdroj[j];
                    }
                }
            
            return vysledek;
        }
    }
}
